package openjdk.jdk8.demo.chap6;

import java.util.*;

import static java.util.stream.Collectors.*;
import static openjdk.jdk8.demo.chap6.Dish.menu;

public class Grouping {

    enum CaloricLevel {DIET, NORMAL, FAT}

    ;

    public static void main(String... args) {
        System.out.println("Dishes grouped by type: " + groupDishesByType());
        System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel());
        System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel());
        System.out.println("Count dishes in groups: " + countDishesInGroups());
        System.out.println("Most caloric dishes by type: " + mostCaloricDishesByType());
        System.out.println("Most caloric dishes by type: " + mostCaloricDishesByTypeWithoutOprionals());
        System.out.println("Sum calories by type: " + sumCaloriesByType());
        System.out.println("Caloric levels by type: " + caloricLevelsByType());
        System.out.println(">>>>>>>>>>>>>>>>>>");
        testLevelGroup(lst);
    }

    private static Map<Dish.Type, List<Dish>> groupDishesByType() {
        return menu.stream().collect(groupingBy(Dish::getType));
    }

    private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {
        return menu.stream().collect(
                groupingBy(dish -> {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                    else return CaloricLevel.FAT;
                }));
    }

    private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        groupingBy((Dish dish) -> {
                            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                            else return CaloricLevel.FAT;
                        })
                )
        );
    }

    private static Map<Dish.Type, Long> countDishesInGroups() {
        return menu.stream().collect(groupingBy(Dish::getType, counting()));
    }

    private static Map<Dish.Type, Optional<Dish>> mostCaloricDishesByType() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)));
    }

    private static Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOprionals() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        collectingAndThen(
                                reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2),
                                Optional::get)));
    }

    private static Map<Dish.Type, Integer> sumCaloriesByType() {
        return menu.stream().collect(groupingBy(Dish::getType,
                summingInt(Dish::getCalories)));
    }

    private static Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType() {
        return menu.stream().collect(
                groupingBy(Dish::getType, mapping(
                        dish -> {
                            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                            else return CaloricLevel.FAT;
                        },
                        toSet())));
    }

    static class ShopCar {
        private int id;
        private int sellerId;
        private String sellerName;
        private String goodsName;
        private int buyerId;
        private String buyerName;
        private int num;

        public ShopCar(int id, int sellerId,
                       String sellerName, String goodsName,
                       int buyerId, String buyerName, int num) {
            this.id = id;
            this.sellerId = sellerId;
            this.sellerName = sellerName;
            this.goodsName = goodsName;
            this.buyerId = buyerId;
            this.buyerName = buyerName;
            this.num = num;
        }

        public String getSellerName() {
            return sellerName;
        }

        public void setSellerName(String sellerName) {
            this.sellerName = sellerName;
        }

        public int getBuyerId() {
            return buyerId;
        }

        public void setBuyerId(int buyerId) {
            this.buyerId = buyerId;
        }

        public String getBuyerName() {
            return buyerName;
        }

        public void setBuyerName(String buyerName) {
            this.buyerName = buyerName;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ShopCar shopCar = (ShopCar) o;
            return id == shopCar.id &&
                    sellerId == shopCar.sellerId &&
                    buyerId == shopCar.buyerId &&
                    num == shopCar.num &&
                    Objects.equals(sellerName, shopCar.sellerName) &&
                    Objects.equals(goodsName, shopCar.goodsName) &&
                    Objects.equals(buyerName, shopCar.buyerName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id, sellerId, sellerName, goodsName, buyerId, buyerName, num);
        }

        @Override
        public String toString() {
            return  "id=" + id +
                    ", sellerId=" + sellerId +
                    ", sellerName='" + sellerName + '\'' +
                    ", goodsName='" + goodsName + '\'' +
                    ", buyerId=" + buyerId +
                    ", buyerName='" + buyerName + '\'' +
                    ", num=" + num ;
        }
    }

    // 初始化数据如下：
    private static List<ShopCar> lst = Arrays.asList(
            new ShopCar(1, 1, "天猫", "华为手机", 1, "dingw", 5),
            new ShopCar(1, 2, "京东", "华为手机", 2, "ly", 2),
            new ShopCar(1, 1, "京东", "小米手机", 3, "zhl", 3),
            new ShopCar(1, 2, "1号店", "华为手机", 1, "dingw", 5),
            new ShopCar(1, 2, "天猫", "苹果手机", 1, "dingw", 2)
    );


    public static void testLevelGroup(List<ShopCar> shopCars) {
        Map<String, Map<String, List<ShopCar>>> result =
                shopCars.stream().collect(groupingBy(ShopCar::getSellerName,
                        groupingBy(ShopCar::getBuyerName)));
        System.out.println(result);
    }


}
